#include <string.h> 
#include <avr/io.h>
#include <ctype.h>

#include "serial.h"
#include "disp.h"

typedef struct SDisplayChar
{
   char mode;
   unsigned char size;
   unsigned char opt;
   unsigned char t_mode;
   unsigned char pwr;
   unsigned char d_time;
   char seg[SEGMENT_DIGIT_MAX];
}
 *psSDisplayChar, sSDisplayChar;


 
/*
     a
   f   b
	 g
   e   c
     d   h
*/

const unsigned char digi_map_t[] = 
{
   /* hgfedcba */
    0b00111111,	/*0*/
	0b00000110,	/*1*/
    0b01011011,	/*2*/
    0b01001111, /*3*/
    0b01100110, /*4*/
    0b01101101, /*5*/
    0b01111101, /*6*/
    0b00000111, /*7*/
    0b01111111, /*8*/
    0b01101111, /*9*/
    0b00000000,  /*all off,10*/
	0b11111111,  /*all on,11*/	
	0b10000000, /*.,12*/
	0b00000001, /*_,13*/
	0b00000010, /*-,14*/
	0b00000011, /*:,15*/
	0b00000100, /*',16*/
	0b01000000, /*-,17*/
	0b01010100, /*n,18*/
	0b01110001, /*f,19*/
	0b01011110, /*d,20*/
	0b00111001, /*c,21*/
	0b00011100, /*u,22*/
	0b01010000, /*r,22*/
	0b00000000 /*Null,23*/
	
};

#define LED_SEG_PORT PORTA
#define LED_SEG_DIR  DDRA

#define LED_POS_PORT PORTB
#define LED_POS_DIR  DDRB

void FlushDispBuff(void);
int PutDispBuff(char mode, char * ptr, unsigned char size);
psSDisplayChar PopDispBuff(void);


//#define TIME_DISP_INTERVAL 3 //2ms
#define TIME_DISP_INTERVAL 2 //2ms
unsigned int time_disp; 
unsigned int time_disp_task;
unsigned int time_disp_update;
unsigned int time_disp_delay = 0;

unsigned char segment_post = 0;	
char DispSegmenBuffer[SEGMENT_DIGIT_MAX];
unsigned char cSegmenPostToggle = 0b00000000;
unsigned char ucDispSegmenUpdate = FALSE;
unsigned char ucDispPowerOn = FALSE;
unsigned char ucDispLampOn = FALSE;

struct 
{
	char seg[SEGMENT_DIGIT_MAX];
	unsigned char opt;	
	unsigned char t_mode;	
	unsigned char pwr;		
	unsigned char d_time;		
}sDispSegmenData = {{SEGMENT_ALL_OFF,SEGMENT_ALL_OFF,SEGMENT_ALL_OFF,SEGMENT_ALL_OFF,SEGMENT_ALL_OFF},0b00000000,FALSE,FALSE,0};


#define DISP_BUFF_MAX 5
sSDisplayChar sDispBuffer[DISP_BUFF_MAX];
unsigned char ucDisplayBuffIn = 0;
unsigned char ucDisplayBuffOut = 0;

#define TIME_DISP_MOTION_TIME  1
struct 
{
	char seg[SEGMENT_DIGIT_MAX];
	unsigned char opt;	
	unsigned char t_mode;	
	unsigned char pwr;		
	unsigned char d_time;		
}sDispMotionData = {{SEGMENT_ALL_OFF,SEGMENT_ALL_OFF,SEGMENT_ALL_OFF,SEGMENT_ALL_OFF,SEGMENT_ALL_OFF},0b00000000,FALSE,FALSE,0};
unsigned char ucDispMotionRun = FALSE;
unsigned char ucDispMotionPos = 0xff;
unsigned int ucDispMotionTime = TIME_DISP_MOTION_TIME;


void InitDisp( void )
{
	/*Segmen Control*/
	LED_SEG_PORT =  _BV(7)|_BV(6)|_BV(5)|_BV(4)|_BV(3)|_BV(2)|_BV(1)|_BV(0); //set pin to high
	LED_SEG_DIR  =  _BV(7)|_BV(6)|_BV(5)|_BV(4)|_BV(3)|_BV(2)|_BV(1)|_BV(0);  //Output mode

	LED_POS_PORT &= ~(_BV(4))|~(_BV(3))|~(_BV(2))|~(_BV(1))|~(_BV(0)); //set pin to low
	LED_POS_DIR  |= _BV(4)|_BV(3)|_BV(2)|_BV(1)|_BV(0);  //Output mode
	
	time_disp = TIME_DISP_INTERVAL; 
	time_disp_task = 33; //3ms * 33 = 99 ms
	time_disp_update = 10; //2ms * 10 = 20ms 
	
	segment_post = 0;
	
    memset((void *)&DispSegmenBuffer,SEGMENT_ALL_ON,sizeof(DispSegmenBuffer));
    
	memset((void *)&sDispSegmenData.seg[0],SEGMENT_ALL_OFF,SEGMENT_DIGIT_MAX);
	sDispSegmenData.opt = 0b00000000;
	sDispSegmenData.t_mode = 0;
	sDispSegmenData.pwr = FALSE;
		
	ucDisplayBuffIn = 0;
	ucDisplayBuffOut = 0;

	ucDispMotionRun = FALSE;
	ucDispMotionPos = 0xff;
	
	ucDispSegmenUpdate = FALSE;		
	
	ucDispPowerOn = FALSE;
}

void TimeDisp( void )
{
	if(time_disp > 0)
	{
		time_disp--;
		if(time_disp==0)
		{
			if(time_disp_update>0)
			{
				time_disp_update--;
			}
					
			if(time_disp_task>0)
			{
				time_disp_task--;
			}
			
			if(time_disp_delay>0)
			{
				time_disp_delay--;
			}
			time_disp = TIME_DISP_INTERVAL;
		}
	}
}

void DispMail(unsigned char act, unsigned char size, char * pbuf)
{
	if((act & 0x80) != 0)
	{
		FlushDispBuff();
		time_disp_delay=0;
		ucDispMotionRun=FALSE;
	}
	pbuf[0] = TRUE;
	PutDispBuff(act&0x7f, pbuf+1, size-1);
}

unsigned char Dec2Seg(unsigned char value)
{

    if( value >= 0 && value <=sizeof(digi_map_t) )
    {
        return ~digi_map_t[value];
    }
    return 0;

}

void FlushDispBuff(void)
{
	ucDisplayBuffIn = 0;
	ucDisplayBuffOut = 0;
	
}

int PutDispBuff(char mode, char * ptr, unsigned char size)
{
	unsigned char in;
	
	in = ucDisplayBuffIn+1;
	
	if (in >= DISP_BUFF_MAX)
		in = 0;
		
	if (in == ucDisplayBuffOut)	// isFull()
	{
		//printf("DF %d\r\n",mode );
		return FALSE;
	}
	

	sDispBuffer[ucDisplayBuffIn].mode = mode;
	sDispBuffer[ucDisplayBuffIn].size = SEGMENT_DIGIT_MAX;
	memcpy((void *)&sDispBuffer[ucDisplayBuffIn].seg, (const void *)ptr, SEGMENT_DIGIT_MAX);
	sDispBuffer[ucDisplayBuffIn].opt    = *(ptr + 5);
	sDispBuffer[ucDisplayBuffIn].t_mode = *(ptr + 6);
	sDispBuffer[ucDisplayBuffIn].pwr = *(ptr + 7);
	sDispBuffer[ucDisplayBuffIn].d_time = *(ptr + 8);
		
	ucDisplayBuffIn = in;
	
	return TRUE;
	
}

psSDisplayChar PopDispBuff(void)
{
	psSDisplayChar ptr;
	unsigned char out;
		
	if( ucDisplayBuffIn == ucDisplayBuffOut )
		return (psSDisplayChar)NULL;
	
	ptr = &sDispBuffer[ucDisplayBuffOut];
	
	out = ucDisplayBuffOut+1;
	if (out >= DISP_BUFF_MAX)
		out = 0;
		
	ucDisplayBuffOut = out;			
	
	return ptr;
}

unsigned int cSegmentToggleTime = 500;
void ProcessDisp(void)
{
	psSDisplayChar ptr;

	if(ucDispMotionRun == FALSE && ucDispSegmenUpdate == FALSE && time_disp_delay==0)
	{	
		if( ucDisplayBuffIn != ucDisplayBuffOut )
		{
			ptr = PopDispBuff(); 
			if( ptr != NULL )
			{
				if(ptr->mode == 0x01) //normal display
				{
					ucDispMotionRun = FALSE;
					ucDispMotionPos = 0xff;		
					memcpy(&sDispSegmenData.seg[0], (const void *)ptr->seg, ptr->size);
					if(ptr->opt < SEGMENT_DIGIT_MAX)
					{
						sDispSegmenData.opt = 0b00000001 << ptr->opt;
					}
					else
					{
						sDispSegmenData.opt = 0;
					}
					sDispSegmenData.t_mode = ptr->t_mode;										
					sDispSegmenData.pwr = ptr->pwr;
					sDispSegmenData.d_time = ptr->d_time;
					ucDispSegmenUpdate = TRUE;			
				}
				else if(ptr->mode == 0x02) //motion display
				{				
					ucDispMotionRun = TRUE;
					ucDispMotionPos = 0xff;		
					memcpy(&sDispMotionData.seg[0], (const void *)ptr->seg, ptr->size);
					if(ptr->opt < SEGMENT_DIGIT_MAX)
					{
						sDispMotionData.opt = 0b00000001 << ptr->opt;
					}
					else
					{
						sDispMotionData.opt = 0;
					}
					sDispMotionData.t_mode = ptr->t_mode;										
					sDispMotionData.pwr = ptr->pwr;
					sDispMotionData.d_time = ptr->d_time;
					ucDispSegmenUpdate = TRUE;			
				}
				else if(ptr->mode == 0x03)
				{
					if(ptr->opt < SEGMENT_DIGIT_MAX)
					{
						sDispSegmenData.opt = 0b00000001 << ptr->opt;
					}
					else
					{
						sDispSegmenData.opt = 0;
					}
					ucDispMotionRun = FALSE;
					ucDispMotionPos = 0xff;		
					sDispSegmenData.t_mode = ptr->t_mode;										
					sDispSegmenData.pwr = ptr->pwr;
					sDispSegmenData.d_time = ptr->d_time;
					ucDispSegmenUpdate = TRUE;			
				}				
			}			
		}
	}
}

void MotionDisp(void)
{
	unsigned char ucCount;
	unsigned char ucMotionPos;
	
	if(ucDispMotionRun == TRUE && ucDispSegmenUpdate == FALSE) 
	{	
		if(ucDispMotionTime > 0)
		{
			ucDispMotionTime--;
			if(ucDispMotionTime == 0)
			{
				ucDispMotionTime = TIME_DISP_MOTION_TIME;

	
				if( ucDispMotionPos == 0xfe)
				{
					ucDispMotionRun = FALSE;
					ucDispMotionPos = 0xff;		
				}		
				else if( ucDispMotionPos == 0xff)
				{			
					memset((void *)&sDispSegmenData.seg[0],SEGMENT_ALL_OFF,SEGMENT_DIGIT_MAX);
					sDispSegmenData.opt = 0;
					sDispSegmenData.t_mode = 0;
					sDispSegmenData.pwr = FALSE;
					ucDispMotionPos = 0;
					ucDispSegmenUpdate = TRUE;		
				}
				else if( ucDispMotionPos < (SEGMENT_DIGIT_MAX-1) )
				{			
					ucMotionPos = 0;
					for( ucCount=0; ucCount<(SEGMENT_DIGIT_MAX-1); ucCount++ )
					{
						// ucDispMotionPos = 0
						// ucCount 		= 0,1,2,3
						// cDispMotionData	= x,x,x,0

						// ucDispMotionPos = 1
						// ucCount 		= 0,1,2,3
						// cDispMotionData	= x,x,0,1
										
						// ucDispMotionPos = 2
						// ucCount 		= 0,1,2,3
						// cDispMotionData	= x,0,1,2

						// ucDispMotionPos = 3
						// ucCount 		= 0,1,2,3
						// cDispMotionData	= 0,1,2,3
										
						if( ucCount < ((SEGMENT_DIGIT_MAX-2) - ucDispMotionPos) )
						{
							sDispSegmenData.seg[ucCount] = SEGMENT_ALL_OFF;						
						}
						else
						{
							//sDispSegmenData.seg[ucCount] = sDispMotionData.seg[ucCount];
							sDispSegmenData.seg[ucCount] = sDispMotionData.seg[ucMotionPos];
							ucMotionPos++;
						}																						
					}		
					ucDispMotionPos++;
					ucDispSegmenUpdate = TRUE;				
				}
				else
				{
					sDispSegmenData.seg[SEGMENT_DIGIT_MAX-1] = sDispMotionData.seg[SEGMENT_DIGIT_MAX-1];
					sDispSegmenData.opt = sDispMotionData.opt;
					sDispSegmenData.t_mode = sDispMotionData.t_mode;
					sDispSegmenData.pwr = sDispMotionData.pwr;			
					sDispSegmenData.d_time = sDispMotionData.d_time;			
					ucDispSegmenUpdate = TRUE;				
					ucDispMotionPos = 0xfe;
				}
			}
		}
	}
}


unsigned int cSegmentToggle = 0;
unsigned char cSegmentOptChar = SEGMENT_BLINK_ON;
void UpdateDisp(void)
{
	char led_pos_pre = LED_POS_PORT;
	char led_segment; 
	
	if(time_disp_update==0)
	{
		time_disp_update=1; //time interval 
		
		
		cSegmentToggle++;
		if( cSegmentToggle >= (cSegmentToggleTime*80)/100) //450 * 2ms = 900 ms
		{
			if( cSegmentToggle >= cSegmentToggleTime ) //500 * 2ms = 1000 ms
			{	
				cSegmentToggle = 0;
				cSegmentOptChar = SEGMENT_BLINK_ON;
			}
			else
			{
				cSegmentOptChar = SEGMENT_BLINK_OFF;
			}
		}
		
		if( (cSegmenPostToggle & (0x01 << segment_post))  != 0 )
		{
			if( cSegmentOptChar == SEGMENT_BLINK_ON)
				led_segment = Dec2Seg( DispSegmenBuffer[segment_post] );			
			else
				led_segment = Dec2Seg( SEGMENT_ALL_OFF );	
		}
		else
			led_segment = Dec2Seg( DispSegmenBuffer[segment_post] );			
		
		if(ucDispPowerOn == TRUE)
		{
			led_segment = 0b11111111;   //all off
			if( cSegmentOptChar == SEGMENT_BLINK_OFF)
			{
				led_segment = 0b11111101;   //0xfd,on dot ditgit in last segmen to show power on 			
				if(ucDispLampOn == TRUE)
				{				
					led_segment &= 0b11111011;  //0xfb  
				}			
			}
			
			//LED_SEG_DIR  = 0x00; //set port to input
			LED_POS_PORT = ((led_pos_pre & 0xe0) | 0x10);									
			LED_SEG_PORT = led_segment;
		}
		else
		{	
			//LED_SEG_DIR  = 0xFF; //set port to output
			if(segment_post < 4)
				LED_POS_PORT = ((led_pos_pre & 0xe0) | (0x08 >> segment_post));
			else
				LED_POS_PORT = ((led_pos_pre & 0xe0) | 0x10);
			LED_SEG_PORT = led_segment;
		}
		
		if( ++segment_post >= SEGMENT_DIGIT_MAX )
		{
			segment_post = 0;
			if( ucDispSegmenUpdate == TRUE )
			{
				memcpy((void *)&DispSegmenBuffer,(const void *)&sDispSegmenData.seg[0],SEGMENT_DIGIT_MAX);
				cSegmenPostToggle = sDispSegmenData.opt;
				ucDispPowerOn = !!(sDispSegmenData.pwr & 0x01);
				ucDispLampOn  = !!(sDispSegmenData.pwr & 0x10);
				time_disp_delay  = sDispSegmenData.d_time * 50; //time = d_time * 2ms * 50 = d_time * 100ms
				if(sDispSegmenData.t_mode == 1)
				{
					cSegmentToggleTime = 300;
				}
				else
				{
					cSegmentToggleTime = 500;
				}				
				ucDispSegmenUpdate = FALSE;
			}
			
		}		
	}
}

void DispTask( void )
{
	UpdateDisp();
			
	if(time_disp_task==0)
	{
		time_disp_task = 33; //3ms * 33 = 99 ms		
		ProcessDisp();
		
		MotionDisp();
		
	}

}
